package Utils;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import java.io.File;
import ArithmeticObjects.VerfGroup;
import ArithUtils.BigNumber;

/**
 * This class defines and implements ProtInfo object, which will contain all data
 * extracted from protInfo.xml file. It holds values as ByteTrees for later use.
 * @author amirzell
 *
 */
public class ProtocolInfo {

	        // private members
	        private byte[] f;
	        private String protocolVersion;
	        private String sessionIdentifier;
	        private int numberOfParties;
	        private int mixServersThreshold;
	        private int bitsInRandomVector;
	        private int statisticalError;
	        private int bitsOfChallenge;    
	        private String hashFunctionForRO; 
	        private String hashFunctionForPRG; 
	        private VerfGroup group;
	        private String pgGroup;
	        private String nameGroup;
	        private ByteTree btGroup;
	        private int widthOfCipherPlainTexts;

	        /**
	         * Constructor, Retrieves and stores all public parameters from an XML protocol info file.
	         * @param fileName
	         * @throws Exception 
	         */
	        public ProtocolInfo(String fileName) throws Exception{
	                File fXmlFile = new File(fileName);
	                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
	                DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
	                Document doc = dBuilder.parse(fXmlFile);
	                doc.getDocumentElement().normalize();
	                Element protocolcInfoElement = doc.getDocumentElement();
	        
	                //extract protocol version
	                String value = getTagValue("version", protocolcInfoElement);
	                this.protocolVersion = value;
	                
	                // extract the globally unique session identifier tied to the generation of a particular joint public key
	                value = getTagValue("sid", protocolcInfoElement);
	                this.sessionIdentifier = value;

	                // extract number of parties
	                value = getTagValue("nopart", protocolcInfoElement);
	                this.numberOfParties = Integer.parseInt(value);

	                // extract threshold
	                value = getTagValue("thres", protocolcInfoElement);
	                this.mixServersThreshold = Integer.parseInt(value);
	                
	                // extract the number of bits in each component of random vectors used for batching in proofs of shuffles and proofs of correct decryption
	                value = getTagValue("vbitlenro", protocolcInfoElement);
	                this.bitsInRandomVector = Integer.parseInt(value);
	                
	                // extract statistical error
	                value = getTagValue("statdist", protocolcInfoElement);
	                this.statisticalError = Integer.parseInt(value);
	                
	                // extract random bits and challenges length
	                value = getTagValue("cbitlenro", protocolcInfoElement);
	                this.bitsOfChallenge = Integer.parseInt(value);

	                // extract RO hashfunction
	                value = getTagValue("rohash", protocolcInfoElement);
	                this.hashFunctionForRO = value;
	                //this.hashFunctionForRO = new HashFunction(value);
	                
	                // extract PRG hashfunction and create PRG
	                value = getTagValue("prg", protocolcInfoElement);
	                this.hashFunctionForPRG = value;
	                
	                // extract pgroup
	                value = getTagValue("pgroup", protocolcInfoElement);
	                this.pgGroup = value;
	                // parse the group comment and group data 
	                int index = value.indexOf("::");
	                value = value.substring(index+2);
	                value = value.replaceAll("\n", "");
	                // convert the group data to byteTree
	                byte [] bytearr = TypeConvertionUtils.hexStringToByteArr(value);
	                ByteTree groupTree = new ByteTree(bytearr);
	                this.btGroup = groupTree;
	                ByteTree groupTypeBt = groupTree.getTheNChild(0); //this is the group type
                	ByteTree groupDataBt = groupTree.getTheNChild(1);// this is the group values
	                if (groupTypeBt.getString().equals("verificatum.arithm.ModPGroup")) {
	                	ByteTree t1 = groupDataBt.getTheNChild(0);
	                	ByteTree t2 = groupDataBt.getTheNChild(1);
	                	ByteTree t3 = groupDataBt.getTheNChild(2);
	                   BigNumber p = t1.getBigNum();
	                   BigNumber q = t2.getBigNum();
	                   BigNumber g = t3.getBigNum();
	                   VerfGroup mdGroup = new VerfGroup (p,q,g);
	                    this.group = mdGroup;
	                    this.nameGroup = "ModPGroup";
	                }
	              else if (groupTypeBt.getString().equals("verificatum.arithm.ECqPGroup"))
	            		{
	                	String ECqPGroupStr = groupDataBt.getString();
	                	VerfGroup ecqGroup = new VerfGroup (ECqPGroupStr);
	                    this.group = ecqGroup;
	                    this.nameGroup = ECqPGroupStr;
	                }

	                // extract default width of ciphertexts processed by mix-net
	                value = getTagValue("width", protocolcInfoElement);
	                this.widthOfCipherPlainTexts = Integer.parseInt(value);
	                
	}
	        
	/**
	 * Extracts value for tag in given XML element.
	 * @param sTag
	 * @param eElement
	 * @return string with value
	 */
	private static String getTagValue(String sTag, Element eElement) {
	        NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes();
	        Node nValue = (Node) nlList.item(0);
	        if (nValue == null)
	        	return "";
	        return nValue.getNodeValue();
	}

	/**
	 * Returns the version of Verificatum used during the execution that produced the proof.
	 */
	public String getVersion() {
	        return protocolVersion;
	}

	/**
	 * Returns the globally unique session identifier tied to the generation of a particular joint public key
	 */
		public String getSid() {
	        return sessionIdentifier;
	}


	/**
	 * Returns the number of parties participating in the mix-net 
	 */
	public int getNumParties(){
	        return numberOfParties;
	}

	/**
	 * Returns the maximum number if mix-servers that must be corrupted to break the privacy of the senders.
	 */
	public int getLambda() {
		return mixServersThreshold;
	}
	
	/**
	 * Returns the number of max ciphers
	 */
	 public int getWidth() {
	        return widthOfCipherPlainTexts;
	}
	
	/**
	 * Returns the number of random bits in each vector used for batching in proofs.   
	 */
	 public int getNe() {
	        return bitsInRandomVector;
	}
	
	/**
	 * returns the acceptable statistical error when sampling random
	 * values. 
	 */
		public int getNr() {
	        return statisticalError;
	}
	
	/**
	 * Returns the number of bits used in the challenge of the verifier
	 * in zero-knowledge proofs
	 */
		public int getNv() {
	        return bitsOfChallenge;
	}
	/**
	 * Returns the HashFunction to be used in the RandomOracle instances.
	 * @return
	 */
	 public String getHashfunction() {	
	        return hashFunctionForRO;
	}
	/**
	 * Returns a PRG instance created with the hash function described in the file.
	 * @return
	 */
	 public String getPRG() {
	        return hashFunctionForPRG;
	}

	/**
	 * Returns the group used in the proofs (modular group or eliptic curve).
	 */
	public VerfGroup getGroup() {
	        return group;
	}
	
	/**
	 * Returns the underlying modular group used in the proofs.
	 */
	public String getGroupStr() {
			return this.pgGroup;
	}
	
	/**
	 * Returns the group data in byteTree.
	 */
	public ByteTree getbtGroup() {
	        return btGroup;
	}

	
	/**
	 * Returns a byte array containing the entire xml file.
	 * @return
	 */
	public byte[] getF(){
	        return f;
	}

	/**
	 * A string representation of the protocol information.
	 */
	public String toString(){
		String str = "---------------------------------------------------";
		str += "\nPROTOCOL INFORMATION";
		str += "\n---------------------------------------------------";
		str += "\n(1)  version    " + this.protocolVersion;
		str += "\n(2)  sid        " + this.sessionIdentifier;
		str += "\n(3)  nopart     " + this.numberOfParties;
		str += "\n(4)  thres      " + this.mixServersThreshold;
		str += "\n(5)  vbitlenro  " + this.bitsInRandomVector;
		str += "\n(6)  statdist   " + this.statisticalError;
		str += "\n(7)  cbitlenro  " + this.bitsOfChallenge;
		str += "\n(8)  rohash     " + this.hashFunctionForRO;
		str += "\n(9)  prg        " + this.hashFunctionForPRG;
		str += "\n(10) pgroup     " + this.nameGroup;
		str += "\n(11) width      " + this.widthOfCipherPlainTexts;
		str += "\n---------------------------------------------------";
		return str;
	}
}
